package com.hjc.project.util.disruptor;

import com.google.common.util.concurrent.ThreadFactoryBuilder;
import com.hjc.project.util.diruptor.OrderEvent;
import com.hjc.project.util.diruptor.OrderEventFactory;
import com.hjc.project.util.diruptor.OrderEventHandler;
import com.hjc.project.util.diruptor.OrderEventProducer;
import com.lmax.disruptor.BlockingWaitStrategy;
import com.lmax.disruptor.RingBuffer;
import com.lmax.disruptor.dsl.Disruptor;
import com.lmax.disruptor.dsl.ProducerType;

import java.nio.ByteBuffer;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class Main {
    public static void main(String[] args) {
        OrderEventFactory orderEventFactory = new OrderEventFactory();
        int ringBufferSize = 2 << 10;
        ExecutorService executor = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors());

        /**
         * 1 eventFactory: 消息(event)工厂对象
         * 2 ringBufferSize: 容器的长度
         * 3 executor: 线程池(建议使用自定义线程池) RejectedExecutionHandler
         * 4 ProducerType: 单生产者 还是 多生产者
         * 5 waitStrategy: 等待策略
         */
        //1. 实例化disruptor对象
        Disruptor<OrderEvent> disruptor = new Disruptor<OrderEvent>(orderEventFactory,
                ringBufferSize,
                // ThreadFactory 作用？？？？ todo
                new ThreadFactoryBuilder().setNameFormat("test-%d").build(),
                ProducerType.SINGLE,
                new BlockingWaitStrategy());

        //2. 添加消费者的监听 (构建disruptor 与 消费者的一个关联关系)
        disruptor.handleEventsWith(new OrderEventHandler());

        //3. 启动disruptor
        disruptor.start();

        //4. 获取实际存储数据的容器: RingBuffer
        RingBuffer<OrderEvent> ringBuffer = disruptor.getRingBuffer();

        OrderEventProducer producer = new OrderEventProducer(ringBuffer);

        ByteBuffer bb = ByteBuffer.allocate(8);

        for(long i = 0 ; i < 100; i ++){
            bb.putLong(0, i);
            System.out.println("生产者 "+Thread.currentThread().getName());
            producer.sendData(bb);
        }

        /**
         * https://blog.csdn.net/weixin_30919571/article/details/96934499
         *
         * disruptor.shutdown方法仅仅能关闭当前已经启动了的消费者线程，对于调用时尚未启动的消费者线程不起作用。在disruptor.shutdown如果能正确的关闭程序，需要满足两个条件：
         *
         * 1. 生产者的生产线程必须执行在disruptor.shutdown方法之前。(另开线程启消费者注意)
         *
         * 2. disruptor.shutdown方法必须执行在所有消费者线程启动之前。
         *
         * 在实际使用中，第二个条件产生的disruptor.shutdown失效问题也许并不常见。原因在于：
         * 线上环境中，生产者线程往往已经运行了一段时间，这段时间内，足够线程池调用所有的消费者线程并运行。
         * 但如果生产者运行的时间过短，导致shutdown提前调用在消费者线程启动之前，则会产生问题。
         */
        disruptor.shutdown();
        executor.shutdown();

    }
}
